package com.haifeng.sbtx.auth.filter;

import com.alibaba.fastjson.JSONObject;
import com.haifeng.sbtx.common.base.ErrorCode;
import com.haifeng.sbtx.common.base.JsonSerializer;
import com.haifeng.sbtx.common.base.Rest;
import com.haifeng.sbtx.common.constant.Constants;
import com.haifeng.sbtx.common.util.CryptoUtil;
import com.haifeng.sbtx.common.util.MessageUtil;
import com.haifeng.sbtx.common.util.RedisUtil;
import com.haifeng.sbtx.mapper.entity.dto.PermissionDto;
import com.haifeng.sbtx.mapper.entity.dto.UserDto;
import com.haifeng.sbtx.mapper.mapper.UserMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;

/**
 * <p>
 *  Token过滤器
 * </p>
 *
 * @author: Haifeng
 * @date: 2019-12-16
 */
@Order(103)
@Slf4j
@AllArgsConstructor
@Component
public class TokenFilter extends OncePerRequestFilter {

    private final UserMapper userMapper;
    private final MessageUtil messageUtil;
    private final RedisUtil redisUtil;
    private final AntPathMatcher antPathMatcher;

    /**
     * 校验token权限
     * @param httpServletRequest
     * @param httpServletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        String authorization = httpServletRequest.getHeader("Authorization");
        httpServletResponse.setContentType("application/json;charset=UTF-8");

        //该过滤器在Security过滤器链后面，所以是经过token有效性认证的,(Beaeer用的会是bearer)
        if (StringUtils.isNotEmpty(authorization) && authorization.toUpperCase().startsWith(Constants.BEARER.toUpperCase())){
            //从token中取出username

            String token = authorization.substring(7);
            String decode = CryptoUtil.decode(token).replaceAll("\"","");
            if (!decode.contains(Constants.DOU)){
                throw new ServletException("token格式不正确");
            }
            String userName = decode.split(",")[0];
            UserDto userDto;
            if (redisUtil.hasKey(Constants.USER_+userName)){
                //从redis中取出用户信息
                String userDtoString = redisUtil.get(Constants.USER_ + userName).toString();
                userDto = JSONObject.parseObject(userDtoString, UserDto.class);
            }else {
                //如果redis取不到则从数据库查询,并放入redis
                userDto = userMapper.getUserByUsername(userName);
                redisUtil.set(Constants.USER_+userName,userDto);
            }
            if (userDto == null){
                throw new ServletException("用户信息不存在");
            }
            Boolean hasPermission = false;
            //如果不是超级管理员并且是需要权限的资源
            if (userDto.getUserType() && !antPathMatcher.match(Constants.NOAUTH,httpServletRequest.getRequestURI())){
                Set<PermissionDto> permissionDtos = userDto.getPermissionDtos();
                //实现权限免注销动态生效，根据用户名从redis中读取权限
                for (PermissionDto permissionDto : permissionDtos) {
                    if (antPathMatcher.match(permissionDto.getPermissionUrl(), httpServletRequest.getRequestURI())) {
                        hasPermission = true;
                        break;
                    }
                }
                if (hasPermission){
                    filterChain.doFilter(httpServletRequest,httpServletResponse);
                    return;
                }else {
                    Rest rest = Rest.error(HttpStatus.UNAUTHORIZED.value(), messageUtil.getMessage(ErrorCode.NO_PERMISSION));
                    httpServletResponse.getWriter().write(JSONObject.toJSONString(rest, JsonSerializer.serializerFeatures));
                    return;
                }
            }
        }
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }
}
